﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Windows.Controls;
using Trinity.Components.Combat;
using Trinity.Components.Combat.Resources;
using Trinity.DbProvider;
using Trinity.Framework;
using Trinity.Framework.Actors.ActorTypes;
using Trinity.Framework.Helpers;
using Trinity.Framework.Objects;
using Trinity.Framework.Reference;
using Trinity.Framework.Avoidance.Structures;
using Trinity.UI;
using Zeta.Common;
using Zeta.Game.Internals.Actors;
using Zeta.Game;



namespace Trinity.Routines.Necromancer
{
    public sealed class NecromancerInariusCorpseLance : NecroMancerBase, IRoutine
    {
        #region Definition

        public string DisplayName => "（耗子)圣套银河尸枪优化版1.4";
        public string Description => "1.4更新内容：亡者领域更加智能化，不再被残血精英触发，领域期间会自动寻找精英。请屏蔽精英中的minion（爪牙）防止领域误触发。" + System.Environment.NewLine +
            "留领域CD起始进度可调，尸矛会尽量贴身释放。偶然出现的小怪卡位现象已经解决。" + System.Environment.NewLine  +
            "天梯build，暂不支持其他技能搭配，低坚韧建议带单手镰刀+暴风盾，萃取双手镰刀。高坚韧建议关闭大部分躲避功能，只躲避冰冻，熔火，强奥等秒人技能";
        public string Author => "Mouse";
        public string Version => "1.4";
        public string Url => "http://www.demonbuddy.cn/thread-12658-1-1.html";
        public Build BuildRequirements => new Build
        {
            Sets = new Dictionary<Set, SetBonus>
            {
                { Sets.GraceOfInarius, SetBonus.Third },
            },
            Skills = new Dictionary<Skill, Rune>
            {
                { Skills.Necromancer.CorpseLance, null },
                { Skills.Necromancer.GrimScythe, null },
            },
        };

        #endregion
        //public override Func<bool> ShouldIgnoreAvoidance => (Skills.Necromancer.LandOfTheDead.IsBuffActive && Player.CurrentHealthPct > 0.6);
        public override Func<bool> ShouldIgnorePackSize => () => (TargetUtil.AnyMobsInRange(10f)&&((PlayerMover.IsBlocked && Core.BlockedCheck.BlockedTime.TotalMilliseconds > 3000) || Core.StuckHandler.IsStuck));
        public override Func<bool> ShouldIgnoreAvoidance => IgnoreCondition;
        private bool IgnoreCondition()
        {
            if ((Skills.Necromancer.LandOfTheDead.IsBuffActive && Player.CurrentHealthPct > 0.6) || Core.Buffs.HasInvulnerableShrine)
                return true;
            return Player.CurrentHealthPct > 0.15 && !Core.Avoidance.InCriticalAvoidance(Player.Position);
        }

        public TrinityPower GetOffensivePower()
        {
            TrinityPower power;
            TrinityActor target;

            if (FindElite(out power))
                return power;

            if (CurrentTarget.IsBoss && Skills.Necromancer.LandOfTheDead.CanCast() && TargetUtil.AnyBossesInRange(20f))
                return LandOfTheDead();

            if (TryLandOfTheDeadPower(out power))
                return power;

            if ((ShouldBoneArmor() || !Skills.Necromancer.BoneArmor.IsBuffActive)&& !Skills.Necromancer.LandOfTheDead.IsBuffActive)
                return BoneArmor();

            if ((Core.Player.PrimaryResourcePct<=0.94 || (Core.Player.CurrentHealthPct < 0.8 && Skills.Necromancer.GrimScythe.TimeSinceUse > 1000) || Skills.Necromancer.GrimScythe.TimeSinceUse > 1800) && ShouldGrimScythe(out target))
                return GrimScythe(target);

            if (TryDecrepifyPower(out power) && !Skills.Necromancer.LandOfTheDead.IsBuffActive)
                return power;

            if (ShouldPrioritizeCorpseLance(out target))
                return CorpseLance(target);

            if (TryBloodRushPower(out power))
                return power;

            if ((!Skills.Necromancer.LandOfTheDead.IsBuffActive && TargetUtil.CorpseCount(80f) <= 0) && ShouldGrimScythe(out target))
                return GrimScythe(target);
            //if (ShouldBloodRush(out Vector3 position))
            //    return BloodRush(position);



            //   if (ShouldBoneArmor() || !Skills.Necromancer.BoneArmor.IsBuffActive)
            //       return BoneArmor();

            if (TryCorpseLancePower(out power))
                return power;



            /*   if (TrySecondaryPower(out power))
                   return power;

               if (TryPrimaryPower(out power))
                   return power;
            */
            return null;
        }


        protected bool FindElite(out TrinityPower power)
        {
            TrinityActor target;
            Vector3 position;
            power = null;

            if (ShouldFindElite(out position))
            {
                power = BloodRush(position);
            }

            return power != null;
        }

       

        protected bool TryBloodRushPower(out TrinityPower power)
        {
            TrinityActor target;
            Vector3 position;
            power = null;

            if (ShouldBloodRush(out position))
            {
                power = BloodRush(position);
            }

            return power != null;
        }
        protected bool TryDecrepifyPower(out TrinityPower power)
        {
            TrinityActor target;
            Vector3 position;
            power = null;

            if (Skills.Necromancer.Decrepify.TimeSinceUse > 4000 && ShouldDecrepify(out target) && (Core.Player.PrimaryResourcePct > 0.94) && (TargetUtil.AnyMobsInRange(20f, 6) || TargetUtil.AnyBossesInRange(20f) || TargetUtil.AnyElitesInRange(20f)))
            {
                power = Decrepify(target);
            }
            return power != null;
        }
        protected bool TryCorpseLancePower(out TrinityPower power)
        {
            TrinityActor target;
            Vector3 position;
            power = null;

            if (ShouldCorpseLance(out target))
            {
                power = CorpseLance(target);
            }
            return power != null;
        }
        protected bool TryLandOfTheDeadPower(out TrinityPower power)
        {
            TrinityActor target;
            Vector3 position;
            power = null;
            if (ShouldLandOfTheDead(out target))
            {
                power = LandOfTheDead();
            }
            return power != null;
        }
        protected override bool ShouldGrimScythe(out TrinityActor target)
        {
            target = null;

            if (!Skills.Necromancer.GrimScythe.CanCast())
                return false;

            //   target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
            target = TargetUtil.BestEliteInRange(15f) ?? TargetUtil.GetBestClusterUnit() ?? TargetUtil.ClosestUnit(15f) ?? CurrentTarget;
            return target != null;
        }

        protected override bool ShouldBloodRush(out Vector3 position)
        {
            position = Vector3.Zero;
            TrinityActor Target;
            if (!Skills.Necromancer.BloodRush.CanCast())
                return false;

            if (Core.Player.CurrentHealthPct < 0.3 && Settings.BloodRushMode == BloodRushUseMode.Default)
                return false;

            if (Skills.Necromancer.BloodRush.CooldownRemaining > 0)
                return false;
            // if (TargetUtil.AnyMobsInRange(12f, 4))
            //     return false;
            Target = TargetUtil.BestEliteInRange(15f) ?? TargetUtil.ClosestUnit(15f);
            if (!IsInCombat || Target == null)
                return false;
            position = TargetUtil.GetBestClusterPoint(15f);

            if (Core.Player.Position.Distance(position) < 40f && Settings.BloodRushMode == BloodRushUseMode.Default)
                return false;
            return position != Vector3.Zero;
        }

        protected bool ShouldFindElite(out Vector3 position)
        {
            position = Vector3.Zero;
            TrinityActor Target;
     
            if (!Skills.Necromancer.BloodRush.CanCast())
                return false;

            if (Skills.Necromancer.BloodRush.CooldownRemaining > 0)
                return false;
            if (TargetUtil.AnyElitesInRange(15f))
                return false;
            if (!TargetUtil.AnyElitesInRange(40f))
                return false;
            Target = TargetUtil.BestEliteInRange(40f) ?? TargetUtil.ClosestUnit(15f);

            if (!IsInCombat || Target == null)
                return false;
            Target = TargetUtil.BestEliteInRange(40f) ?? TargetUtil.ClosestUnit(15f);
            position = Target.Position;

            return position != Vector3.Zero;
        }

        protected override bool ShouldCorpseLance(out TrinityActor target)
        {
            target = null;
            if (!Skills.Necromancer.CorpseLance.CanCast())
                return false;

            if (Skills.Necromancer.LandOfTheDead.IsBuffActive)
            {
                target = TargetUtil.BestEliteInRange(15f) ?? TargetUtil.ClosestUnit(15f);
                return target != null;
            }

            if (TargetUtil.CorpseCount(80f) <= 0)
                return false;

            target = TargetUtil.BestEliteInRange(15f) ?? TargetUtil.ClosestUnit(15f);

            return target != null;
        }

        protected override bool ShouldLandOfTheDead(out TrinityActor target)
        {
            target = null;
            if (!Skills.Necromancer.LandOfTheDead.CanCast())
                return false;
            if (!TargetUtil.AnyBossesInRange(15f))
            {
                target = TargetUtil.BestEliteInRange(15f);
                //if (target.HitPointsPct<60 || (target == null || !TargetUtil.AnyElitesInRange(15f) || Core.Rift.CurrentProgressionPct >= Settings.RProgression))
                if (target == null || !TargetUtil.AnyElitesInRange(15f) || Core.Rift.CurrentProgressionPct >= Settings.RProgression || target.HitPointsPct < 0.60)
                    return false;
            }

            /*     if (TargetUtil.AnyElitesInRange(15f) && Core.Rift.CurrentProgressionPct < Settings.RProgression)
                 {
                     if (TargetUtil.BestEliteInRange(15f).HitPointsPct < 0.50)
                         return false;
                 }
                 */
            target = TargetUtil.BestEliteInRange(15f) ?? TargetUtil.ClosestUnit(15f) ?? CurrentTarget;
            return target != null;
        }


        protected override bool ShouldDecrepify(out TrinityActor target)
        {
            target = null;
            if (!Skills.Necromancer.Decrepify.CanCast())
                return false;

            if (Core.Player.PrimaryResourcePct < 0.94)
                return false;

            // todo: investigate why cant find the power on monsters.         

            // anti-spam workaround
            if (Skills.Necromancer.Decrepify.TimeSinceUse < 5000)
                return false;

            target = TargetUtil.BestTargetWithoutDebuff(20f, SNOPower.P6_Necro_Decrepify);
            return target != null;
        }

        protected override bool ShouldBoneArmor()
        {
            if (!Skills.Necromancer.BoneArmor.CanCast())
                return false;

            if (!TargetUtil.AnyMobsInRange(15f, 3) && !TargetUtil.AnyElitesInRange(15f) && !TargetUtil.AnyBossesInRange(15f))
                return false;

            // todo Need a way to check the cooldown properly, PowerManager/CanCast doesnt seem to be catching it.
            if (Skills.Necromancer.BoneArmor.CooldownRemaining > 0 || Core.Player.PrimaryResourcePct< 0.9)
                return false;

            if (Settings.BoneArmorMode == BoneArmorUseMode.Default && Skills.Necromancer.BoneArmor.IsBuffActive)
                return false;
            // Inarius Set inceases damage making it more useful than just for defence
            // Wisdom of Kalan ring increases stacks by 5

            //if (Core.Buffs.GetBuff(SNOPower.P6_Necro_BoneArmor)?.Remaining.TotalSeconds > 0)
            //    return false;

            return true;
            }


        private bool ShouldApplyCurseWithScythe()
        {
            // Inarius build with Shadowhook should rarely run out of resource to force primary,
            // so it needs to be occasionally prioritized to apply curses.
            return Runes.Necromancer.CursedScythe.IsActive && Skills.Necromancer.GrimScythe.TimeSinceUse > 2000;
        }

        protected override bool ShouldCommandSkeletons(out TrinityActor target)
        {
            if (base.ShouldCommandSkeletons(out target))
            {
                return Skills.Necromancer.CommandSkeletons.TimeSinceUse > 5000;
            }
            return false;
        }
        protected bool ShouldPrioritizeCorpseLance(out TrinityActor target)
        {
            target = null;

            if (!Skills.Necromancer.CorpseLance.CanCast())
                return false;

            if (Skills.Necromancer.LandOfTheDead.IsBuffActive || TargetUtil.CorpseCount(80f) >= 3)
            {
                target = TargetUtil.BestEliteInRange(15f) ?? TargetUtil.ClosestUnit(15f);
            }

            return target != null;
        }
        public TrinityPower GetDefensivePower()
        {
            return GetBuffPower();
        }

        public TrinityPower GetBuffPower()
        {
            if (Player.CurrentHealthPct < 0.25 && Skills.Necromancer.BloodRush.CanCast())
            {
                return BloodRush(Avoider.SafeSpot);
            }
            // Put up bone armor when running around with high cluster size setting and not yet fighting
          if (ShouldBoneArmor())
                {
                return BoneArmor();
                }
              
            return null;
        }

        public TrinityPower GetDestructiblePower()
        {
            return DefaultDestructiblePower();
        }

        protected  bool NewTryBloodrushMovement(Vector3 destination, out TrinityPower trinityPower)
        {
            trinityPower = null;

            if (!Skills.Necromancer.BloodRush.IsActive)
                return false;

            var path = Core.DBNavProvider.CurrentPath;
            if (path != null && path.Contains(destination) && Skills.Necromancer.BloodRush.CanCast())
            {
                var projectedPosition = IsBlocked
                    ? Core.Grids.Avoidance.GetPathCastPosition(50f, true)
                    : Core.Grids.Avoidance.GetPathWalkPosition(50f, true);

                if (projectedPosition != Vector3.Zero)
                {
                    var distance = projectedPosition.Distance(Player.Position);
                    var inFacingDirection = Core.Grids.Avoidance.IsInPlayerFacingDirection(projectedPosition, 90);
                    if ((distance > 15f || IsBlocked) && inFacingDirection)
                    {
                        trinityPower = BloodRush(projectedPosition);
                        return true;
                    }
                }
            }
            return false;
        }

        public TrinityPower GetMovementPower(Vector3 destination)
        {
            TrinityPower power;
            /* if (Skills.Necromancer.LandOfTheDead.IsBuffActive && (TargetUtil.AnyBossesInRange(15f) || TargetUtil.AnyElitesInRange(15f)))
             {
                 if (TryCorpseLancePower(out power))
                     return CorpseLance(power);
             }
             */
            if (Skills.Necromancer.BoneArmor.TimeSinceUse > 30000 && TargetUtil.AnyMobsInRange(25f))
            {
                power = BoneArmor();
                return power;
            }
            if (!IsInCombat || CurrentTarget != null && CurrentTarget.IsElite && CurrentTarget.Position.Distance(destination) <= 10f)
            {
                if (NewTryBloodrushMovement(destination, out power))
                    return power;
            }

            TrinityActor target;
            /*   if (Skills.Necromancer.Decrepify.CanCast() && (Core.Player.PrimaryResourcePct > 0.9) && (TargetUtil.AnyMobsInRange(60f, 4) || !TargetUtil.AnyBossesInRange(60f)))
                   {
                   target = TargetUtil.ClosestUnit(50f);
                   if (target != null)
                       {
                       return Decrepify(target);
                       }
                   }
               */

            /*
                if (TargetUtil.AnyMobsInRange(10f) && Skills.Necromancer.GrimScythe.CanCast())
                   {
                   target = TargetUtil.ClosestUnit(10f);
                   if (target != null)
                       {
                       return GrimScythe(TargetUtil.ClosestUnit(10f));
                       }
                   }
       */
            return Walk(destination);
        }



        #region Settings      

        public override int ClusterSize => Settings.ClusterSize;
        public override float EmergencyHealthPct => Settings.EmergencyHealthPct;

        IDynamicSetting IRoutine.RoutineSettings => Settings;
        public NecromancerInariusCorpseLanceSettings Settings { get; } = new NecromancerInariusCorpseLanceSettings();

        public enum BoneArmorUseMode
        {
            None = 0,
            Default,
            Always,
        }
        public enum BloodRushUseMode
        {
            None = 0,
            Default,
            Always,
        }
        public int RProgression;
        public sealed class NecromancerInariusCorpseLanceSettings : NotifyBase, IDynamicSetting
        {

            private BoneArmorUseMode _BoneArmorModE;
            private BloodRushUseMode _BloodRushModE;
            private int _clusterSize;
            private float _emergencyHealthPct;
            private int _RProgressioN;
            [DefaultValue(BoneArmorUseMode.Always)]
            public BoneArmorUseMode BoneArmorMode
            {
                get { return _BoneArmorModE; }
                set { SetField(ref _BoneArmorModE, value); }
            }

            [DefaultValue(BloodRushUseMode.Always)]
            public BloodRushUseMode BloodRushMode
            {
                get { return _BloodRushModE; }
                set { SetField(ref _BloodRushModE, value); }
            }

            [DefaultValue(8)]
            public int ClusterSize
            {
                get { return _clusterSize; }
                set { SetField(ref _clusterSize, value); }
            }

            [DefaultValue(0.4f)]
            public float EmergencyHealthPct
            {
                get { return _emergencyHealthPct; }
                set { SetField(ref _emergencyHealthPct, value); }
            }

            [DefaultValue(75)]
            public int RProgression
            {
                get { return _RProgressioN; }
                set { SetField(ref _RProgressioN, value); }
            }

            public override void LoadDefaults()
            {
                base.LoadDefaults();

            }

            #region IDynamicSetting

            public string GetName() => GetType().Name;
            public UserControl GetControl() => UILoader.LoadXamlByFileName<UserControl>(GetName() + ".xaml");
            public object GetDataContext() => this;
            public string GetCode() => JsonSerializer.Serialize(this);
            public void ApplyCode(string code) => JsonSerializer.Deserialize(code, this, true);
            public void Reset() => LoadDefaults();
            public void Save() { }

            #endregion
        }

        #endregion


    }
}


